using System;
using System.Collections.Generic;
using System.Xml;

namespace BasecampRestAPI
{
	/*
	<project>
	  <id type="integer">#{id}</id>
	  <name>#{name}</name>
	  <created-on type="datetime">#{created_on}</created-on>
	  <status>#{status}</status>
	  <last-changed-on type="datetiem">#{last_changed_on}</last-changed-on>
	  <company>
		<id type="integer">#{id}</id>
		<name>#{name}</name>
	  </company>

	  <!-- if user is administrator, or show_announcement is true -->
	  <announcement>#{announcement}</announcement>

	  <!-- if user is administrator -->
	  <start-page>#{start_page}</start-page>
	  <show-writeboards type="boolean">#{show_writeboards}</show-writeboards>
	  <show-announcement type="boolean">#{show_announcement}</show-announcement>
	</project>
	*/
	public class Project : IProject
	{
		public static string PathForId(int id)
		{
			return string.Format("projects/{0}.xml", id);
		}

		private readonly IRestWebService _service;

		public static IProject GetInstance(IBaseCamp baseCamp, XmlNode node)
		{
			return new Project(baseCamp, baseCamp.Service, node);
		}
		private Project(IBaseCamp baseCamp, IRestWebService service, XmlNode node)
		{
			Camp = baseCamp;
			_service = service;
			Id = XmlHelpers.ParseInteger(node, "id");
			Name = XmlHelpers.ChildNodeText(node, "name");
			CreatedOn = XmlHelpers.ParseDateTime(node, "created-on");
			LastChangedOn = XmlHelpers.ParseDateTime(node, "last-changed-on", CreatedOn);
			Announcement = XmlHelpers.ChildNodeText(node, "announcement");
			Status = XmlHelpers.ChildNodeText(node, "status");
			StartPage = XmlHelpers.ChildNodeText(node, "start-page");
			ShowWriteBoards = XmlHelpers.ParseBool(node, "show-writeboards");
			ShowAnnouncement = XmlHelpers.ParseBool(node, "show-announcement");
		}

		private IBaseCamp Camp { get; set; }

		private string RequestPathForAction(string action)
		{
			return string.Format("projects/{0}/{1}", Id, action);
		}

		#region Implementation of IProject
		public int Id { get; private set; }
		public string Name { get; private set; }
		public DateTime CreatedOn { get; private set; }
		public DateTime LastChangedOn { get; private set; }
		public string Announcement { get; private set; }
		public string Status { get; private set; }
		public string StartPage { get; private set; }
		public bool ShowWriteBoards { get; private set; }
		public bool ShowAnnouncement { get; private set; }
		public IPerson[] People
		{
			get
			{
				string requestPath = RequestPathForAction("people.xml");
				List<IPerson> people = new List<IPerson>();
				foreach (XmlNode node in _service.GetRequestResponseElement(requestPath).ChildNodes)
				{
					people.Add(Person.GetInstance(Camp, node));
				}
				return people.ToArray();
			}
		}

		public IToDoList[] ToDoLists
		{
			get
			{
				string requestPath = RequestPathForAction("todo_lists.xml?filter=all");
				List<IToDoList> lists = new List<IToDoList>();
				foreach (XmlNode node in _service.GetRequestResponseElement(requestPath).ChildNodes)
				{
					lists.Add(ToDoList.GetInstance(Camp, node));
				}
				return lists.ToArray();
			}
		}

		public ICategory[] GetCategoriesForType(CategoryType type)
		{
			string requestPath = RequestPathForAction(
				string.Format("categories.xml?type={0}", type.ToString().ToLower()));
			List<ICategory> categories = new List<ICategory>();
			foreach (XmlNode node in _service.GetRequestResponseElement(requestPath).ChildNodes)
			{
				categories.Add(Category.GetInstance(Camp, node));
			}
			return categories.ToArray();
		}

		public ICategory CreateCategory(CategoryType type, string name)
		{
			string requestPath = RequestPathForAction("categories.xml");
			string data = string.Format(Properties.Resources.CreateCategoryRequest,
				XmlHelpers.EscapeXml(type.ToString().ToLower()), name);
			string location = _service.PostRequestGetLocation(requestPath, data);
			return Category.GetInstance(Camp, _service.GetRequestResponseElement(location));
		}

		public IPost[] GetMessagesForCategory(ICategory category)
		{
			return MessagesForAction(string.Format("cat/{0}/posts.xml", category.Id));
		}

		public IPost[] GetArchivedMessagesForCategory(ICategory category)
		{
			return MessagesForAction(string.Format("cat/{0}/posts/archive.xml", category.Id));
		}

		public IPost CreateMessage(ICategory category, string title, string body, string extendedBody, bool isPrivate, IEnumerable<IPerson> notifications, IEnumerable<IAttachment> attachments)
		{
			string requestPath = RequestPathForAction("posts.xml");
			string extra = GetNotificationXml(notifications) + GetAttachmentsXml(attachments);
			string data = string.Format(Properties.Resources.CreateMessageRequest, category.Id, title, body, extendedBody, isPrivate, extra);
			string location = _service.PostRequestGetLocation(requestPath, data);
			return Post.GetInstance(Camp, _service.GetRequestResponseElement(location));
		}

		public IMilestone[] Milestones
		{
			get
			{
				string requestPath = RequestPathForAction("milestones/list.xml");
				List<IMilestone> milestones = new List<IMilestone>();
				foreach (XmlNode node in _service.GetRequestResponseElement(requestPath, "<request/>").ChildNodes)
				{
					milestones.Add(Milestone.GetInstance(Camp, node));
				}
				return milestones.ToArray();
			}
		}

		public IPost[] RecentMessages
		{
			get
			{
				return MessagesForAction("posts.xml");
			}
		}

		public IPost[] ArchivedMessages
		{
			get
			{
				return MessagesForAction("posts/archive.xml");
			}
		}

		public IMilestone GetMilestoneById(int id)
		{
			foreach (IMilestone milestone in Milestones)
			{
				if (milestone.Id == id)
				{
					return milestone;
				}
			}
			throw new ArgumentException();
		}

		public IMilestone CreateMilestone(string title, DateTime deadline, IPerson responsibleParty, bool notify)
		{
			string requestPath = RequestPathForAction("milestones/create");
			string data = string.Format(Properties.Resources.CreateMilestoneRequest,
				XmlHelpers.EscapeXml(title), XmlHelpers.DateString(deadline),
				responsibleParty.Id, notify.ToString().ToLower());
			return Milestone.GetInstance(Camp,
				_service.GetRequestResponseElement(requestPath, data, HttpVerb.Post).FirstChild);
		}

		public IToDoList CreateToDoList(string name, string description, IMilestone milestone, bool privateList, bool trackedList)
		{
			string requestPath = RequestPathForAction("todo_lists.xml");
			string data;
			if (milestone != null)
			{
				data = string.Format(Properties.Resources.CreateToDoListWithMilestoneRequest,
					XmlHelpers.EscapeXml(name), XmlHelpers.EscapeXml(description),
					milestone.Id, privateList.ToString().ToLower(), trackedList.ToString().ToLower());
			}
			else
			{
				data = string.Format(Properties.Resources.CreateToDoListRequest,
					XmlHelpers.EscapeXml(name), XmlHelpers.EscapeXml(description),
					privateList.ToString().ToLower(), trackedList.ToString().ToLower());
			}
			string location = _service.PostRequestGetLocation(requestPath, data);
			return ToDoList.GetInstance(Camp, _service.GetRequestResponseElement(location));
		}

		#endregion

		private IPost[] MessagesForAction(string action)
		{
			List<IPost> messages = new List<IPost>();
			foreach (XmlNode node in _service.GetRequestResponseElement(RequestPathForAction(action)).ChildNodes)
			{
				messages.Add(Post.GetInstance(Camp, node));
			}
			return messages.ToArray();
		}

		private static string GetNotificationXml(IEnumerable<IPerson> notifications)
		{
			string result = string.Empty;
			foreach (IPerson person in notifications)
			{
				result += string.Format("<notify>{0}</notify>\n", person.Id);
			}
			return result;
		}

		private static string GetAttachmentsXml(IEnumerable<IAttachment> attachments)
		{
			string result = string.Empty;
			foreach (IAttachment attachment in attachments)
			{
				result += string.Format(Properties.Resources.AttachmentTemplate,
					attachment.Name, attachment.UploadId,
					attachment.ContentType, attachment.OriginalFileName);
			}
			return result;
		}
	}
}